#include "./include/producer_consumer.h"
#include "./include/S_printModule@windows.h"
#include "./include/Semaphore_t.h"
#include <iostream>
#include <conio.h>
#include <windows.h>
#include <string.h>

using namespace std;

#define WIN_TEST // 定义一个在window平台测试的宏

/*
 * 函数声明
 */
DWORD WINAPI call_producer(LPVOID lpParamter);  // 呼叫生产者，由生产者线程来执行
DWORD WINAPI call_consumer(LPVOID lpParamter);  // 呼叫消费者，由消费者线程来执行

/*
 * 全局变量（控制变量）
 * 以下全局变量只有主线程可写，其他线程只读
 * 主要的作用是让主线程控制其他线程
 */
int isProducerSuspended=0;  // 标志生产者线程是否被挂起，初始值为0，表示未被挂起
int isConsumerSuspended=0;  // 标志消费者线程是否被挂起，初始值为0，表示未被挂起
int produceSpeed=500;       // 生产者每生产一个数据就睡眠 500 毫秒，表示生产数据的速度
int consumeSpeed=500;       // 消费者每消费一个数据就睡眠 500 毫秒，表示消费数据的速度
int exit_flag=1;  // 控制程序的退出


int main()
{
    // 初始化控制台
    initConsole();

    /*
     * 创建生产者和消费者线程
     */
    HANDLE producer_h=CreateThread(NULL,0,call_producer,NULL,0,NULL);
    HANDLE consumer_h=CreateThread(NULL,0,call_consumer,NULL,0,NULL);

    /*
     * 主线程作为总控，与用户进行交互
     * 根据用户输入的命令控制生产者、消费者线程的挂起、恢复、以及生产数据的速度
     */
    char buf_in[20]={};
    int isEnd=0;
    OutputStorage outstore(10, 60);

    HANDLE out_h=GetStdHandle(STD_OUTPUT_HANDLE);

    extern COORD cur_input_position;
    extern COORD input_position;
    extern Semaphore_t console_mutex;
    extern CONSOLE_SCREEN_BUFFER_INFO screen_bInfo;

    while( exit_flag ) {

        // P(&console_mutex);
        // SetConsoleCursorPosition(out_h, cur_input_position);
        // showCursor();
        // Sleep(500);
        // V(&console_mutex);

        if( kbhit() ) {

            P(&console_mutex);              // 互斥访问控制台

            showCursor();
            SetConsoleCursorPosition(out_h, cur_input_position);
            Sleep(10);
            cin.getline(buf_in,20);
            fflush(stdin);                  // 刷新输入缓冲区
            if('@'!=buf_in[0]) {
                outstore.addOneStr(buf_in, 1);
            }

            if(!cin) {
                outstore.addOneStr("    [error]: maybe input too long!", 0);
                outstore.updataInputRegion(input_position,isEnd);
                V(&console_mutex); // 释放控制台操作
                continue;
            }
            switch (buf_in[0])
            {
                case '@':
                    exit_flag=0;
                    isEnd=1;
                    outstore.addOneStr("Bye Bye!!!",0);
                    break;
                case 'P':
                case 'p':
                    outstore.cmdParse(isProducerSuspended,produceSpeed,buf_in,producer_h);
                    break;
                case 'C':
                case 'c':
                    outstore.cmdParse(isConsumerSuspended,consumeSpeed,buf_in,consumer_h);
                    break;
                default:
                    outstore.addOneStr("    unknow command, please check and input again.",0);
            }// end switch(buf_in[0])

            outstore.updataInputRegion(input_position,isEnd);
            V(&console_mutex); // 释放控制台操作

        }// end if( kbhit() )
    }// end while( exit_flag )

    Sleep(1000);
    // COORD end_position;
    // end_position.X=0;
    // end_position.Y=input_position.Y+12;
    // SetConsoleCursorPosition(out_h,end_position);
    system("cls");
    return 0;
}

/*
 * 生产者线程执行单元
 */
DWORD WINAPI call_producer(LPVOID lpParamter)
{
    /*
     * 初始化输出控制
     */
    #ifdef WIN_TEST
        extern COORD p_position;
        extern Semaphore_t console_mutex;
        OutputStorage producer_outstore(OUTPUT_REGION_SIZE-1,80);
        char str[80]={};
    #endif

    /*
     * 生产数据
     */
    int state=0;  // 状态，0 表示正常运行，非 0 表示不运行
    int index=0;  // 记录产生的数据加入到缓冲区之后的下标
    while(exit_flag) {
        if( !isProducerSuspended ) {

            #ifdef WIN_TEST
                producer_outstore.addOneStr("    producer create a data!",0);
                extern Semaphore_t empty;
                if(0>=getSemaphoreValue(&empty)) {
                    producer_outstore.addOneStr("    full, waiting......",0);
                    P(&console_mutex);
                    producer_outstore.updataOutputRegion(p_position);
                    V(&console_mutex);
                }
            #endif

            state=0;
            producer(index);       // 生产者不知疲倦地生产数据

            #ifdef WIN_TEST
                sprintf(str, "    producer has added a data to buffer[%d]!",index);
                producer_outstore.addOneStr(str,0);
                // producer_outstore.addOneStr(" ",0);
                P(&console_mutex);
                producer_outstore.updataOutputRegion(p_position);
                V(&console_mutex);
            #endif

            Sleep(produceSpeed);  // 偶尔躺平一下下
        } else if(0==state) {

            #ifdef WIN_TEST
                producer_outstore.addOneStr("    Suspended!",0);
                P(&console_mutex);
                producer_outstore.updataOutputRegion(p_position);
                V(&console_mutex);
            #endif

            state=1;
            SuspendThread( GetCurrentThread() );  // 挂起线程
        }
    }
}

/*
 * 消费者线程执行单元
 */
DWORD WINAPI call_consumer(LPVOID lpParamter)
{
    /*
     * 初始化输出控制
     */
    extern COORD c_position;
    extern Semaphore_t console_mutex;
    OutputStorage consumer_outstore(OUTPUT_REGION_SIZE-1,80);
    char str[80]={};

    Sleep(2000);     // 先躺平2秒，让生产者先生产一会数据
    int state=0;     // 状态，0 表示正常运行，非 0 表示不运行
    int index=0;     // 记录消费的数据加入到缓冲区之后的下标
    while(exit_flag) {
        if( !isConsumerSuspended ) {

            #ifdef WIN_TEST
                extern Semaphore_t full;
                if(0>=getSemaphoreValue(&full)) {
                    consumer_outstore.addOneStr("    empty, waiting.....",0);
                    P(&console_mutex);
                    consumer_outstore.updataOutputRegion(c_position);
                    V(&console_mutex);
                }
            #endif

            state=0;
            consumer(index);      // 消费者不知疲倦地消费数据
            Sleep(consumeSpeed);  // 偶尔躺平一下下

            #ifdef WIN_TEST
                sprintf(str, "    consumer has removed a data from buffer[%d]!",index);
                consumer_outstore.addOneStr(str,0);
                consumer_outstore.addOneStr("    consumer using the data!",0);
                // consumer_outstore.addOneStr(" ",0);
                P(&console_mutex);
                consumer_outstore.updataOutputRegion(c_position);
                V(&console_mutex);
            #endif
        } else if(0==state) {

            #ifdef WIN_TEST
                consumer_outstore.addOneStr("    Suspended!",0);
                P(&console_mutex);
                consumer_outstore.updataOutputRegion(c_position);
                V(&console_mutex);
            #endif

            state=1;
            SuspendThread( GetCurrentThread() );
        }
    }
}
